package com.imooc.xunwu.service.house;

import com.imooc.xunwu.base.HouseStatus;
import com.imooc.xunwu.base.LoginUserUtil;
import com.imooc.xunwu.entity.*;
import com.imooc.xunwu.repository.*;
import com.imooc.xunwu.service.ServiceMultiResult;
import com.imooc.xunwu.service.ServiceResult;
import com.imooc.xunwu.service.search.ISearchService;
import com.imooc.xunwu.web.dto.HouseDTO;
import com.imooc.xunwu.web.dto.HouseDetailDTO;
import com.imooc.xunwu.web.dto.HousePictureDTO;
import com.imooc.xunwu.web.form.DatatableSearch;
import com.imooc.xunwu.web.form.HouseForm;
import com.imooc.xunwu.web.form.PhotoForm;
import com.imooc.xunwu.web.form.RentSearch;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import org.elasticsearch.search.SearchService;
import org.modelmapper.ModelMapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import java.util.*;

/**
 * @Auther: hyr
 * @Date: 2018/10/25 14:43
 * @Description:
 */
@Service
public class HouseServiceImpl implements IHouseService {


    @Autowired
    private ModelMapper modelMapper;

    @Autowired
    private HouseDetailRepository houseDetailRepository;

    @Autowired
    private HouseRepository houseRepository;

    @Autowired
    private SubwayRepository subwayRepository;

    @Autowired
    private HousePictureRepository housePictureRepository;

    @Autowired
    private SubwayStationRepository subwayStationRepository;

    @Autowired
    private HouseTagRepository houseTagRepository;

    @Value("${qiniu.cdn.prefix}")
    private String cdnPrefix;

    @Autowired
    private IQiNiuService qiNiuService;

    @Autowired
    private ISearchService searchService;

    /**
     * 新增房源信息
     * @param houseForm
     * @return
     */
    @Override
    public ServiceResult<HouseDTO> save(HouseForm houseForm) {
        HouseDetail detail = new HouseDetail();
        ServiceResult<HouseDTO> subwayValidtionResult = wrapperDetailInfo(detail, houseForm);
        if (subwayValidtionResult != null) {
            return subwayValidtionResult;
        }

        House house = new House();
        modelMapper.map(houseForm, house);

        Date now = new Date();
        house.setCreateTime(now);
        house.setLastUpdateTime(now);
        house.setAdminId(LoginUserUtil.getLoginUserId());
        house = houseRepository.save(house);

        detail.setHouseId(house.getId());
        detail = houseDetailRepository.save(detail);

        List<HousePicture> pictures = generatePictures(houseForm, house.getId());
        Iterable<HousePicture> housePictures = housePictureRepository.save(pictures);

        HouseDTO houseDTO = modelMapper.map(house, HouseDTO.class);
        HouseDetailDTO houseDetailDTO = modelMapper.map(detail, HouseDetailDTO.class);

        houseDTO.setHouseDetail(houseDetailDTO);

        List<HousePictureDTO> pictureDTOS = new ArrayList<>();
        housePictures.forEach(housePicture -> pictureDTOS.add(modelMapper.map(housePicture, HousePictureDTO.class)));
        houseDTO.setPictures(pictureDTOS);
        houseDTO.setCover(this.cdnPrefix + houseDTO.getCover());

        List<String> tags = houseForm.getTags();
        if (tags != null && !tags.isEmpty()) {
            List<HouseTag> houseTags = new ArrayList<>();
            for (String tag : tags) {
                houseTags.add(new HouseTag(house.getId(), tag));
            }
            houseTagRepository.save(houseTags);
            houseDTO.setTags(tags);
        }

        return new ServiceResult<HouseDTO>(true, null, houseDTO);
    }

    /**
     * 封装图片信息
     * @param form
     * @param houseId
     * @return
     */
    private List<HousePicture> generatePictures(HouseForm form, Long houseId){
        List<HousePicture> pictures = new ArrayList<>();
        if (form.getPhotos() == null || form.getPhotos().isEmpty()){
            return pictures;
        }
        for (PhotoForm photoForm : form.getPhotos()){
            HousePicture picture = new HousePicture();
            picture.setHouseId(houseId);
            picture.setCdnPrefix(cdnPrefix);
            picture.setPath(photoForm.getPath());
            picture.setWidth(photoForm.getWidth());
            picture.setHeight(photoForm.getHeight());
            pictures.add(picture);
        }

        return pictures;
    }

    /**
     * 封装房源详细信息
     * @param houseDetail
     * @param houseForm
     * @return
     */
    private ServiceResult<HouseDTO> wrapperDetailInfo(HouseDetail houseDetail, HouseForm houseForm){
        Subway subway = subwayRepository.findOne(houseForm.getSubwayLineId());
        if (subway == null){
            return new ServiceResult<>(false,"Not vaile subway line");
        }
        SubwayStation subwayStation = subwayStationRepository.findOne(houseForm.getSubwayStationId());
        if (subwayStation == null || subway.getId() !=subwayStation.getSubwayId()){
            return new ServiceResult<>(false,"Not vaile subwaystation");
        }
        houseDetail.setSubwayLineId(subway.getId());
        houseDetail.setSubwayLineName(subway.getName());

        houseDetail.setSubwayStationId(subwayStation.getId());
        houseDetail.setSubwayStationName(subwayStation.getName());

        houseDetail.setDescription(houseForm.getDescription());
        houseDetail.setDetailAddress(houseForm.getDetailAddress());
        houseDetail.setLayoutDesc(houseDetail.getLayoutDesc());
        houseDetail.setRentWay(houseDetail.getRentWay());
        houseDetail.setRoundService(houseDetail.getRoundService());
        houseDetail.setTraffic(houseDetail.getTraffic());

        return null;
    }


    /**
     * 管理员查询
     * @param searchBody
     * @return
     */
    @Override
    public ServiceMultiResult<HouseDTO> adminQuery(DatatableSearch searchBody) {
        List<HouseDTO> houseDTOS = new ArrayList<>();
        Sort sort = new Sort(Sort.Direction.fromString(searchBody.getDirection()),searchBody.getOrderBy());
        int page = searchBody.getStart() / searchBody.getLength();
        Pageable pageable = new PageRequest(page,searchBody.getLength(),sort);

        Specification<House> specification = (root, query, cb) -> {
            Predicate predicate = cb.equal(root.get("adminId"), LoginUserUtil.getLoginUserId());
            predicate = cb.and(predicate, cb.notEqual(root.get("status"), HouseStatus.DELETED.getValue()));
            if (searchBody.getCity() != null) {
                predicate = cb.and(predicate, cb.equal(root.get("cityEnName"), searchBody.getCity()));
            }
            if (searchBody.getStatus() != null) {
                predicate = cb.and(predicate, cb.equal(root.get("status"), searchBody.getStatus()));
            }
            if (searchBody.getCreateTimeMin() != null) {
                predicate = cb.and(predicate, cb.greaterThanOrEqualTo(root.get("createTime"), searchBody.getCreateTimeMin()));
            }
            if (searchBody.getCreateTimeMax() != null) {
                predicate = cb.and(predicate, cb.lessThanOrEqualTo(root.get("createTime"), searchBody.getCreateTimeMax()));
            }
            if (searchBody.getTitle() != null) {
                predicate = cb.and(predicate, cb.like(root.get("title"), "%" + searchBody.getTitle() + "%"));
            }
            return predicate;
        };

        Page<House> houses = houseRepository.findAll(specification, pageable);
        houses.forEach(house -> {
            HouseDTO houseDTO = modelMapper.map(house, HouseDTO.class);
            houseDTO.setCover(this.cdnPrefix + house.getCover());
            houseDTOS.add(houseDTO);
        });
        return new ServiceMultiResult<>(houses.getTotalElements(),houseDTOS);
    }


    /**
     * 修改房源信息
     * @param houseForm
     * @return
     */
    @Override
    @Transactional
    public ServiceResult update(HouseForm houseForm) {
        House house = this.houseRepository.findOne(houseForm.getId());
        if (house == null) {
            return ServiceResult.notFound();
        }

        HouseDetail detail = this.houseDetailRepository.findByHouseId(house.getId());
        if (detail == null) {
            return ServiceResult.notFound();
        }

        ServiceResult wrapperResult = wrapperDetailInfo(detail, houseForm);
        if (wrapperResult != null) {
            return wrapperResult;
        }

        houseDetailRepository.save(detail);

        List<HousePicture> pictures = generatePictures(houseForm, houseForm.getId());
        housePictureRepository.save(pictures);

        if (houseForm.getCover() == null) {
            houseForm.setCover(house.getCover());
        }

        modelMapper.map(houseForm, house);
        house.setLastUpdateTime(new Date());
        houseRepository.save(house);

        if (house.getStatus() == HouseStatus.PASSES.getValue()){
            searchService.index(house.getId());
        }

        return ServiceResult.success();
    }

    /**
     * 查询完整房源信息
     *
     * @param id
     * @return
     */
    @Override
    public ServiceResult<HouseDTO> findCompleteOne(Long id) {
        House house = houseRepository.findOne(id);
        if (house == null){
            return ServiceResult.notFound();
        }
        HouseDetail detail = houseDetailRepository.findByHouseId(house.getId());
        List<HousePicture> pictures = housePictureRepository.findAllByHouseId(house.getId());

        HouseDetailDTO detailDTO = modelMapper.map(detail,HouseDetailDTO.class);
        List<HousePictureDTO> pictureDTOS = new ArrayList<>();
        for (HousePicture picture : pictures){
            HousePictureDTO pictureDTO = modelMapper.map(picture,HousePictureDTO.class);
            pictureDTOS.add(pictureDTO);
        }
        List<HouseTag> tags = houseTagRepository.findAllByHouseId(house.getId());
        List<String> tagList = new ArrayList<>();
        for (HouseTag tag : tags){
            tagList.add(tag.getName());
        }
        HouseDTO result = modelMapper.map(house,HouseDTO.class);
        result.setHouseDetail(detailDTO);
        result.setPictures(pictureDTOS);
        result.setTags(tagList);
        return ServiceResult.of(result);
    }

    /**
     * 移除图片
     *
     * @param id
     * @return
     */
    @Override
    @Transactional
    public ServiceResult removePhoto(Long id) {
        HousePicture picture = housePictureRepository.findOne(id);
        if (picture == null){
            return ServiceResult.notFound();
        }

        try {
            Response response =this.qiNiuService.delete(picture.getPath());
            if (response.isOK()){
                housePictureRepository.delete(id);
                return ServiceResult.success();
            }else {
                return new ServiceResult(false,response.error);
            }
        } catch (QiniuException e) {
            e.printStackTrace();
            return new ServiceResult(false,e.getMessage());
        }
    }

    /**
     * 更新封面
     *
     * @param coverId
     * @param targetId
     * @return
     */
    @Override
    @Transactional
    public ServiceResult updateCover(Long coverId, Long targetId) {
        HousePicture cover = housePictureRepository.findOne(coverId);
        if (cover == null){
            return ServiceResult.notFound();
        }

        houseRepository.updateCover(targetId,cover.getPath());
        return ServiceResult.success();
    }

    /**
     * 新增标签
     *
     * @param houseId
     * @param tag
     * @return
     */
    @Override
    @Transactional
    public ServiceResult addTag(Long houseId, String tag) {
        House house = houseRepository.findOne(houseId);
        if (house == null) {
            return ServiceResult.notFound();
        }

        HouseTag houseTag = houseTagRepository.findByNameAndHouseId(tag, houseId);
        if (houseTag != null) {
            return new ServiceResult(false, "标签已存在");
        }

        houseTagRepository.save(new HouseTag(houseId, tag));
        return ServiceResult.success();
    }

    /**
     * 移除标签
     *
     * @param houseId
     * @param tag
     * @return
     */
    @Override
    @Transactional
    public ServiceResult removeTag(Long houseId, String tag) {
        House house = houseRepository.findOne(houseId);
        if (house == null) {
            return ServiceResult.notFound();
        }

        HouseTag houseTag = houseTagRepository.findByNameAndHouseId(tag, houseId);
        if (houseTag == null) {
            return new ServiceResult(false, "标签不存在");
        }

        houseTagRepository.delete(houseTag.getId());
        return ServiceResult.success();
    }

    /**
     * 更新房源状态
     *
     * @param id
     * @param status
     * @return
     */
    @Override
    @Transactional
    public ServiceResult updateStatus(Long id, int status) {
        House house = houseRepository.findOne(id);
        if (house == null) {
            return ServiceResult.notFound();
        }

        if (house.getStatus() == status) {
            return new ServiceResult(false, "状态没有发生变化");
        }

        if (house.getStatus() == HouseStatus.RENTED.getValue()) {
            return new ServiceResult(false, "已出租的房源不允许修改状态");
        }

        if (house.getStatus() == HouseStatus.DELETED.getValue()) {
            return new ServiceResult(false, "已删除的资源不允许操作");
        }

        houseRepository.updateStatus(id, status);

        // 上架更新索引 其他情况都要删除索引
        if (status == HouseStatus.PASSES.getValue()) {
            searchService.index(id);
        } else {
            searchService.remove(id);
        }
        return ServiceResult.success();
    }

    /**
     * 加入预约清单
     *
     * @param houseId
     * @return
     */
    @Override
    public ServiceResult addSubscribeOrder(Long houseId) {
        return null;
    }

    /**
     * 预约看房时间
     *
     * @param houseId
     * @param orderTime
     * @param telephone
     * @param desc
     * @return
     */
    @Override
    public ServiceResult subscribe(Long houseId, Date orderTime, String telephone, String desc) {
        return null;
    }

    /**
     * 取消预约
     *
     * @param houseId
     * @return
     */
    @Override
    public ServiceResult cancelSubscribe(Long houseId) {
        return null;
    }

    /**
     * 完成预约
     *
     * @param houseId
     */
    @Override
    public ServiceResult finishSubscribe(Long houseId) {
        return null;
    }

    /**
     * 查询房源信息
     * @param rentSearch
     * @return
     */
    @Override
    public ServiceMultiResult<HouseDTO> query(RentSearch rentSearch) {
        if (rentSearch.getKeywords() != null){
            ServiceMultiResult serviceResult  = searchService.query(rentSearch);
            if (serviceResult.getTotal() == 0){
                return new ServiceMultiResult<>(0,new ArrayList<>());
            }
            return new ServiceMultiResult<>(serviceResult.getTotal(),wrapperHouseResult(serviceResult.getResult()));
        }

        return simplequery(rentSearch);
    }

    public ServiceMultiResult<HouseDTO> simplequery(RentSearch rentSearch) {
        Sort sort = new Sort(Sort.Direction.DESC,"lastUpdateTime");
        int page = rentSearch.getStart();

        Pageable pageable = new PageRequest(page,rentSearch.getSize(),sort);

        Specification<House> specification = (root, criteriaQuery, criteriaBuilder) -> {
            Predicate predicate =  criteriaBuilder.equal(root.get("status"),HouseStatus.PASSES.getValue());
            predicate = criteriaBuilder.and(predicate,criteriaBuilder.equal(root.get("cityEnName"),rentSearch.getCityEnName()));
            return predicate;
        };

        Page<House> houses = houseRepository.findAll(specification,pageable);
        List<HouseDTO> houseDTOS = new ArrayList<>();
        houses.forEach(house -> {
            HouseDTO houseDTO = modelMapper.map(house,HouseDTO.class);
            houseDTO.setCover(this.cdnPrefix + house.getCover());
            houseDTOS.add(houseDTO);
        });

        return new ServiceMultiResult<>(houses.getTotalElements(),houseDTOS);
    }

    private List<HouseDTO> wrapperHouseResult(List<Long> houseIds) {
        List<HouseDTO> result = new ArrayList<>();
        Map<Long,HouseDTO> idToHouseMap = new HashMap<>();
        Iterable<House>houses = houseRepository.findAll(houseIds);
        houses.forEach(house -> {
            HouseDTO houseDTO = modelMapper.map(house,HouseDTO.class);
            houseDTO.setCover(this.cdnPrefix +house.getCover());
            idToHouseMap.put(house.getId(),houseDTO);
        });
        wrapperHouseList(houseIds,idToHouseMap);

        //矫正顺序
        for (Long houseId : houseIds){
            result.add(idToHouseMap.get(houseId));
        }
        return result;
    }


    /**
     * 渲染详细信息 及 标签
     * @param houseIds
     * @param idToHouseMap
     */
    private void wrapperHouseList(List<Long> houseIds, Map<Long, HouseDTO> idToHouseMap) {
        List<HouseDetail> details = houseDetailRepository.findAllByHouseIdIn(houseIds);
        details.forEach(houseDetail -> {
            HouseDTO houseDTO = idToHouseMap.get(houseDetail.getHouseId());
            HouseDetailDTO detailDTO = modelMapper.map(houseDetail, HouseDetailDTO.class);
            houseDTO.setHouseDetail(detailDTO);
        });

        List<HouseTag> houseTags = houseTagRepository.findAllByHouseIdIn(houseIds);
        houseTags.forEach(houseTag -> {
            HouseDTO house = idToHouseMap.get(houseTag.getHouseId());
            house.getTags().add(houseTag.getName());
        });
    }
}
